package cn.com.chengmandian.bike.utils;




import cn.com.chengmandian.core.web.exceptions.BadResponseException;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;

import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.*;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.math.BigDecimal;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class WxPayUtil {

    private static Map<String,String> serialNos = new HashMap<>();

    /**
     * jsapi统一下单
     */
    public static Map<String, String> jsapiTransactions(String appId,
                                                        String mchId,
                                                        String openId,
                                                        String title,
                                                        String outTradeNo,
                                                        BigDecimal money,
                                                        String keyPath,
                                                        String certPath,
                                                        String notifyUrl) throws Exception {
        return jsapiTransactions(appId, mchId, openId, title, outTradeNo, money, null, keyPath, certPath, notifyUrl);
    }

    /**
     * jsapi统一下单
     */
    public static Map<String, String> jsapiTransactions(String appId,
                                                        String mchId,
                                                        String openId,
                                                        String title,
                                                        String outTradeNo,
                                                        BigDecimal money,
                                                        String timeExpire,
                                                        String keyPath,
                                                        String certPath,
                                                        String notifyUrl) throws Exception {
        log.info("开始下单：{}", outTradeNo);
        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                .setAppid(appId)
                .setMchid(mchId)
                .setDescription(title)
                .setOut_trade_no(outTradeNo)
                .setNotify_url(notifyUrl)
                .setAmount(new Amount().setTotal(money.multiply(new BigDecimal(100)).intValue()))
                .setPayer(new Payer().setOpenid(openId));
        if (StrUtil.isNotEmpty(timeExpire)) {
            unifiedOrderModel.setTime_expire(timeExpire);
        }
        log.info("统一下单参数 {}", unifiedOrderModel);
        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethod.POST,
                WxDomain.CHINA.toString(),
                WxApiType.JS_API_PAY.toString(),
                mchId,
                getSerialNumber(certPath),
                null,
                keyPath,
                JSONUtil.toJsonStr(unifiedOrderModel)
        );
        log.info("统一下单响应 {}", response);

        if (response.getStatus() != 200) {
            throw new BadResponseException(response.getBody());
        }

        String body = response.getBody();
        JSONObject result = JSONObject.parseObject(body);
        String prepayId = result.getString("prepay_id");
        Map<String, String> map = WxPayKit.jsApiCreateSign(appId, prepayId, keyPath);
        log.info("唤起支付参数:{}", map);
        return map;
    }
    /**
     * 解析支付响应结果
     */
    public static String parseNotify(HttpServletRequest request, String apiKey3, String platformCertPath) throws Exception {
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serialNo = request.getHeader("Wechatpay-Serial");
        String signature = request.getHeader("Wechatpay-Signature");
        if (signature.contains("WECHATPAY")) {
            log.error("微信支付响应猜测为签名探测流量:{}", signature);
        }

        log.info("timestamp:{} nonce:{} serialNo:{} signature:{} apiKey3:{} platformCertPath:{}", timestamp, nonce, serialNo, signature, apiKey3, platformCertPath);
        String result = HttpKit.readData(request);
        // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
        String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                apiKey3, platformCertPath);

        log.info("微信通知明文 {}", plainText);
        return plainText;
    }

    /**
     * 退款
     */
    public static Map<String, String> refund(String mchId,
                              String outRefundNo,
                              String outTradeNo,
                              String reason,
                              BigDecimal refundMoney,
                              BigDecimal orderMoney,
                              String keyPath,
                              String certPath,
                              String notifyUrl) throws Exception {
        log.info("开始退款: {}", outRefundNo);
        RefundModel refundModel = new RefundModel()
                .setOut_trade_no(outTradeNo)
                .setOut_refund_no(outRefundNo)
                .setReason(reason)
                .setNotify_url(notifyUrl)
                .setAmount(new RefundAmount().setRefund(refundMoney.multiply(new BigDecimal(100)).intValue()).setTotal(orderMoney.multiply(new BigDecimal(100)).intValue()).setCurrency("CNY"))
                .setFunds_account("AVAILABLE");
        log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethod.POST,
                WxDomain.CHINA.toString(),
                WxApiType.DOMESTIC_REFUNDS.toString(),
                mchId,
                getSerialNumber(certPath),
                null,
                keyPath,
                JSONUtil.toJsonStr(refundModel)
        );
        // 根据证书序列号查询对应的证书来验证签名结果
        log.info("退款响应 {}", response);
        if (response.getStatus() != 200) {
            throw new BadResponseException(response.getBody());
        }
        String body = response.getBody();
        JSONObject result = JSONObject.parseObject(body);
        return MapUtil.of("refund_id", result.getString("refund_id"));
    }
    /**
     * 支付分退款
     */
    public static Map<String, String> refundPayScore(String mchId,
                                             String outRefundNo,
                                             String transactionId,
                                             String reason,
                                             BigDecimal refundMoney,
                                             BigDecimal orderMoney,
                                             String keyPath,
                                             String certPath,
                                             String notifyUrl) throws Exception {
        log.info("开始退款: {}", outRefundNo);
        RefundModel refundModel = new RefundModel()
                .setTransaction_id(transactionId)
                .setOut_refund_no(outRefundNo)
                .setReason(reason)
                .setNotify_url(notifyUrl)
                .setAmount(new RefundAmount().setRefund(refundMoney.multiply(new BigDecimal(100)).intValue()).setTotal(orderMoney.multiply(new BigDecimal(100)).intValue()).setCurrency("CNY"))
                .setFunds_account("AVAILABLE");
        log.info("退款参数 {}", JSONUtil.toJsonStr(refundModel));
        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethod.POST,
                WxDomain.CHINA.toString(),
                WxApiType.DOMESTIC_REFUNDS.toString(),
                mchId,
                getSerialNumber(certPath),
                null,
                keyPath,
                JSONUtil.toJsonStr(refundModel)
        );
        // 根据证书序列号查询对应的证书来验证签名结果
        log.info("退款响应 {}", response);
        if (response.getStatus() != 200) {
            throw new BadResponseException(response.getBody());
        }
        String body = response.getBody();
        JSONObject result = JSONObject.parseObject(body);
        return MapUtil.of("refund_id", result.getString("refund_id"));
    }

    /**
     * 效验证书
     */
    public static String getSerialNumber(String certPath) {
        if (StrUtil.isEmpty(certPath) || StrUtil.isBlank(certPath)) {
            throw new BadResponseException("请维护支付证书路径");
        }
        if (!serialNos.containsKey(certPath)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(certPath));
            serialNos.put(certPath, certificate.getSerialNumber().toString(16).toUpperCase());
        }
        return serialNos.get(certPath);
    }

    public static void main(String[] args) {
        String certPath = "D:/apiclient_platform.pem";
        System.out.println(getSerialNumber(certPath));
        BufferedInputStream inputStream = FileUtil.getInputStream(certPath);
        X509Certificate certificate = PayKit.getCertificate(inputStream);
        System.out.println(certificate.getSerialNumber().toString(16).toUpperCase());
    }
}
